package com.autonavi.collapse.task.context;

import com.autonavi.collapse.task.annotation.TaskComponent;
import com.autonavi.collapse.task.context.utils.ResolverUtil;

import java.lang.reflect.Method;
import java.util.*;

public class DefaultScanContextManager implements ContextManager {

    private Map<Class<?>, List<String>> typeMap = new HashMap<>();
    private Map<String, Object> components = new HashMap<>();

    public DefaultScanContextManager(String... packages) throws Exception{
        List<Class<?>> classes;
        if (packages != null && packages.length > 0){
            classes = ResolverUtil.loadClasses(packages);
            for (Class<?> aClass : classes){
                initComponent(aClass);
            }
        }
    }

    private void initComponent(Class<?> aClass) throws Exception{
        TaskComponent annotation = aClass.getDeclaredAnnotation(TaskComponent.class);
        if (annotation != null){
            String name = annotation.value();
            if (name.isEmpty()){
                name = UUID.randomUUID().toString();
            }
            Object instance = aClass.newInstance();
            Class<?>[] interfaces = aClass.getInterfaces();
            for (Class<?> anInterface : interfaces){
                List<String> names = typeMap.computeIfAbsent(anInterface, k -> new ArrayList<>());
                names.add(name);
            }
            List<String> names = typeMap.computeIfAbsent(aClass, k -> new ArrayList<>());
            names.add(name);
            components.put(name,instance);
        }
    }

    @Override
    public void init() throws Exception {
        for (Object component : components.values()){
            Class<?> aClass = component.getClass();
            TaskComponent annotation = aClass.getDeclaredAnnotation(TaskComponent.class);
            if (annotation != null) {
                String initMethod = annotation.initMethod();
                if (!initMethod.isEmpty()) {
                    Method method = aClass.getDeclaredMethod(initMethod);
                    if (method != null) {
                        method.invoke(component);
                    }
                }
            }
        }
    }

    @Override
    public <T> T getComponentByType(Class<T> type) {
        List<String> names = typeMap.get(type);
        if (names != null && !names.isEmpty()) {
            String name = names.get(0);
            return (T) components.get(name);
        }
        return null;
    }

    @Override
    public Object getComponentByName(String name) {
        return components.get(name);
    }

    @Override
    public <T> Map<String, T> getComponentsByType(Class<T> type) {
        Map<String, T> result = new HashMap<>();
        List<String> names = typeMap.get(type);
        if (names != null) {
            for (String name : names) {
                Object component = components.get(name);
                if (component != null) {
                    result.put(name, (T) component);
                }
            }
        }
        return result;
    }
}
